#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <stdint.h>
#include <time.h>
#include "simulation.h"
#define NUMRUNS 1

double average(vector<double> input) {
	double mean = 0.0;
	for (uint32_t index = 0; index < input.size(); ++index) {
		mean += input[index];
	}
	mean /= input.size();
	return mean;
}

double stdev(vector<double> input, double mean) {
	double var = 0.0;
	for (uint32_t index = 0; index < input.size(); ++index) {
		var += ((input[index] - mean) * (input[index] - mean));
	}
	var /= (input.size()-1);
	return sqrt(var);
}

void test1() {
	vector<double> input;
	for (uint32_t index = 0; index < 10; ++index) {
		input.push_back(index);
	}
	cout << "mean = " << average(input) << endl;
	cout << "stdev = " << stdev(input, average(input)) << endl;
}

void get_mean_conf_delta(vector<double> data, double *databar, double *conf_delta) {
	*databar = average(data);
	double datastdev = stdev(data, *databar);
	*conf_delta = 1.96 * datastdev / sqrt(30);
}

int main(int argc, char **argv) {

	if (argc != 7) {
		cout << "./queuesimulation <lambda> <KCPU> <KIO> <C> <L> <M>" << endl;
		exit(0);
	}
	Simulation *simulation[NUMRUNS];
	uint32_t l = atoi(argv[5]);
	uint32_t m = atoi(argv[6]);

	if (l == 0 && (m < 0 || m > 5)) {
		cout << "Invalid value of M" << endl;
		exit(0);
	}
	vector<double> avg_system_times, avg_waiting_times;
	vector<double> running_times;
	vector<double> clrs;
	if (l == 0) {
		if (m == 0) {
			cout << "FCFS Simulation " << endl;
		} else if (m == 1) {
			cout << "LCFS Simulation " << endl;
		} else if (m == 2) {
			cout << "SJF Simulation " << endl;
		} else if (m == 3) {
			cout << "Priority Non-preemptive simulation " << endl;
		} else if (m == 4) {
			cout << "Priority Preemptive simulation " << endl;
		}
	} else if (l == 1) {
		cout << "Web server simulation " << endl;
	}
	double start_time, end_time;
	start_time = clock();
	for (uint32_t index = 0 ;index < NUMRUNS; ++index) {
		cout << "Run " << index + 1 << endl;
		if (l == 0) {
			if (m == 0) {
				simulation[index] = new FCFSSimulation(argc, argv);
			} else if (m == 1) {
				simulation[index] = new LCFSSimulation(argc, argv);
			} else if (m == 2) {
				simulation[index] = new SJFSimulation(argc, argv);
			} else if (m == 3) {
				simulation[index] = new PriorityNPSimulation(argc, argv);
			} else if (m == 4) {
				simulation[index] = new PriorityPSimulation(argc, argv);
			}
		} else if (l == 1) {
			simulation[index] = new WebServerSimulation(argc, argv);
		} else {
			cout << "Invalid value of L. L can either be 0 or 1" << endl;
		}
		simulation[index]->run();
		simulation[index]->calculate_statistics();
		avg_system_times.push_back(simulation[index]->avg_system_time);
		avg_waiting_times.push_back(simulation[index]->avg_waiting_time);
		clrs.push_back(simulation[index]->clr);
	}
	end_time = clock();				// simulation end timer
	running_times.push_back(1.0 * (end_time - start_time) / CLOCKS_PER_SEC);
	cout << endl;

	cout << "Value of the input parameter lambda = " << simulation[0]->lambda << endl;
	cout << "Value of the input parameter K_CPU = " << simulation[0]->kcpu << endl;
	cout << "Value of the input parameter K_IO = " << simulation[0]->kio << endl;
	cout << "Value of the input parameter C = " << simulation[0]->c << endl;
	cout << "Value of the master clock at the end of the simulation = " 
		<< simulation[0]->sim_time << endl;

	char queue_names[4][50] = {"one", "two", "three", "four"};
	if (l == 0) {
		double mean, conf_delta;
		if (m == 3 || m == 4) {
			vector<double> queue_avg_waiting_times, queue_clrs;//, queue_avg_system_times;
			for (uint32_t queue_num = 0; queue_num < 4; queue_num++) {
				for (uint32_t index = 0; index < NUMRUNS; ++index) {
					queue_avg_waiting_times.push_back(simulation[index]->queue_avg_waiting_time[queue_num]);
					queue_clrs.push_back(simulation[index]->queue_clr[queue_num]);
					//queue_avg_system_times.push_back(simulation[index]->queue_avg_system_time[queue_names[queue_num]]);
				}

				get_mean_conf_delta(queue_avg_waiting_times, &mean, &conf_delta);
				cout << "Average waiting time for queue " << queue_names[queue_num] 
					<< " = " << mean << ", Confidence Interval ("
					<< mean - conf_delta << ", "
					<< mean + conf_delta << ")" << endl;

				get_mean_conf_delta(queue_clrs, &mean, &conf_delta);
				cout << "Average CLR for queue " << queue_names[queue_num] 
					<< " = " << mean << ", Confidence Interval ("
					<< mean - conf_delta << ", "
					<< mean + conf_delta << ")" << endl;

				queue_avg_waiting_times.clear();
				queue_clrs.clear();
			}
		}
		get_mean_conf_delta(avg_system_times, &mean, &conf_delta);

		cout << "Average system time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(avg_waiting_times, &mean, &conf_delta);
		cout << "Average waiting time = " << mean << ", Confidence Interval ("
			<< mean - conf_delta << ", "
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(clrs, &mean, &conf_delta);

		cout << "Average CLR = " << mean << ", Confidence Interval ("
			<< mean - conf_delta << ", "
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(running_times, &mean, &conf_delta);

		cout << "Running time = " << mean << ", Confidence Interval ("
			<< mean << ", "
			<< mean << ")" << endl;
	} else if (l == 1) {
		vector<double> avg_waiting_times;
		vector<double> avg_system_times;
		vector<double> cpu_avg_waiting_times;
		vector<double> io_1_avg_waiting_times;
		vector<double> io_2_avg_waiting_times;
		vector<double> io_3_avg_waiting_times;
		
		vector<double> clrs;
		vector<double> cpu_clrs;
		vector<double> io_1_clrs;
		vector<double> io_2_clrs;
		vector<double> io_3_clrs;
		vector<double> cpu_visits;
		vector<double> io_1_visits;
		vector<double> io_2_visits;
		vector<double> io_3_visits;

		double mean, conf_delta;

		for (uint32_t index = 0; index < NUMRUNS; ++index) {
			avg_system_times.push_back(simulation[index]->avg_system_time);
			avg_waiting_times.push_back(simulation[index]->avg_waiting_time);
			cpu_avg_waiting_times.push_back(simulation[index]->queue_avg_waiting_time[0]);
			io_1_avg_waiting_times.push_back(simulation[index]->queue_avg_waiting_time[1]);
			io_2_avg_waiting_times.push_back(simulation[index]->queue_avg_waiting_time[2]);
			io_3_avg_waiting_times.push_back(simulation[index]->queue_avg_waiting_time[3]);

			clrs.push_back(simulation[index]->clr);
			cpu_clrs.push_back(simulation[index]->queue_clr[0]);
			io_1_clrs.push_back(simulation[index]->queue_clr[1]);
			io_2_clrs.push_back(simulation[index]->queue_clr[2]);
			io_3_clrs.push_back(simulation[index]->queue_clr[3]);

			cpu_visits.push_back(simulation[index]->visits[0]);
			io_1_visits.push_back(simulation[index]->visits[1]);
			io_2_visits.push_back(simulation[index]->visits[2]);
			io_3_visits.push_back(simulation[index]->visits[3]);
		}

		get_mean_conf_delta(avg_system_times, &mean, &conf_delta);
		cout << "Average system time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(avg_waiting_times, &mean, &conf_delta);
		cout << "Overall average waiting time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(cpu_avg_waiting_times, &mean, &conf_delta);
		cout << "CPU average waiting time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_1_avg_waiting_times, &mean, &conf_delta);
		cout << "IO one average waiting time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_2_avg_waiting_times, &mean, &conf_delta);
		cout << "IO two average waiting time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_3_avg_waiting_times, &mean, &conf_delta);
		cout << "IO three average waiting time = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(clrs, &mean, &conf_delta);
		cout << "Overall system CLR  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(cpu_clrs, &mean, &conf_delta);
		cout << "CPU CLR  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_1_clrs, &mean, &conf_delta);
		cout << "IO one CLR  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_2_clrs, &mean, &conf_delta);
		cout << "IO two CLR  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_3_clrs, &mean, &conf_delta);
		cout << "IO three CLR  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(cpu_visits, &mean, &conf_delta);\
		cout << "Avg CPU visits  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_1_visits, &mean, &conf_delta);
		cout << "Avg IO one visits  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_2_visits, &mean, &conf_delta);
		cout << "Avg IO two visits = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

		get_mean_conf_delta(io_3_visits, &mean, &conf_delta);
		cout << "Avg IO three visits  = " << mean << ", Confidence Interval (" 
			<< mean - conf_delta << ", " 
			<< mean + conf_delta << ")" << endl;

	}
}
